using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.Serialization;
using System.IO;

namespace Musicman.Client.Utilities
{
    public delegate void AlertDownloaderMessage(string message);

    public class AutomaticDownloader 
    {
        private AutomaticDownloaderSettings settings;
        private FileManagerService.FileManagerService fmService;
        private FileManagerService.FileDownloadHeader fdHeader;
        private long personID;
        private Thread _downloadThred = null;
        private int _loopTime;
        private List<AutomaticDownloadEntity> downloadList;
        public event AlertDownloaderMessage ActionMessage;


        public AutomaticDownloaderSettings Settings
        {
            get { return settings; }
            set { settings = value; }
        }

        public AutomaticDownloader(AutomaticDownloaderSettings settings, 
                                   FileManagerService.FileManagerService fmService,
                                   long personID)
        {
            this.settings = settings;
            this.fmService = fmService;
            this.personID = personID;
            fdHeader = new Musicman.Client.FileManagerService.FileDownloadHeader();
            this._loopTime = this.settings.RetryMin * 60000;
            this.downloadList = new List<AutomaticDownloadEntity>();
        }

        public void RunDownloadJob()
        {
            while (this.settings.UseDownloader && this.downloadList.Count != 0)
            {
                try
                {
                    const int buffersize = 1024 * 16;
                    AutomaticDownloadEntity [] entities = this.downloadList.ToArray();
                    for (int i = 0; i < entities.Length; i++)
                    {
                        AutomaticDownloadEntity entity = entities[i];
                        entity.RetryTimes++;
                        if (entity.RetryTimes >= this.settings.RetryTimes)
                        {
                            if (this.settings.FailureAction == AutomaticDownloaderSettings.SAVE_TO_DOWNLOADS)
                            {
                                fmService.AddPendingDownloads(this.personID, new long[] { entity.FileID });
                                AlertMessage(String.Format("File {0} saved to pending downloads", entity.SourceFileName));
                            }
                            else
                            {
                                AlertMessage(String.Format("File {0} can't be downloaded", entity.SourceFileName));
                            }
                            downloadList.Remove(entity);
                        }
                        else
                        {
                            string destName = String.Format("{0}\\{1}", entity.CopyDestination, entity.SourceFileName);
                            FileStream destFs = null;
                            FileStream sourceFs = null;

                            byte[] buffer = new byte[buffersize];
                            try
                            {
                                destFs = File.Open(destName, FileMode.Create, FileAccess.Write);
                                if (File.Exists(entity.SourcePath))
                                {
                                    sourceFs = File.Open(entity.SourcePath, FileMode.Open, FileAccess.Read);
                                    int count = sourceFs.Read(buffer, 0, buffersize);
                                    while (count > 0)
                                    {
                                        destFs.Write(buffer, 0, count);
                                        count = sourceFs.Read(buffer, 0, buffersize);
                                    }
                                    sourceFs.Close();
                                    destFs.Close();
                                    downloadList.Remove(entity);
                                    AlertMessage(String.Format("File {0} downloaded", entity.SourceFileName));
                                }
                                else // we are not in adequate local network, try to get file via web service
                                {
                                    this.fdHeader.exists = false;
                                    fmService.FileDownloadHeaderValue = fdHeader;
                                    byte[] fileContent = fmService.DownloadFile(this.personID, entity.FileID);
                                    if (fmService.FileDownloadHeaderValue.exists)
                                    {
                                        int length = 0;
                                        for (int j = 0; j < fileContent.Length / buffersize; j++)
                                        {
                                            length = (j + 1) * buffersize > fileContent.Length ? fileContent.Length % buffersize : buffersize;
                                            destFs.Write(fileContent, j * buffersize, length);
                                        }
                                        downloadList.Remove(entity);
                                        AlertMessage(String.Format("File {0} downloaded", entity.SourceFileName));
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                string message = ex.Message;
                                string innerMessage = (ex.InnerException == null) ? "" : ex.InnerException.Message;
                                fmService.LogClientException(this.personID, "Error copying file", message, innerMessage);
                                downloadList.Remove(entity);
                            }
                            finally
                            {
                                if (destFs != null) destFs.Close();
                                if (sourceFs != null) sourceFs.Close();
                            }
                        }
                    }
                    this._loopTime = this.settings.RetryMin * 60000;
                    Thread.Sleep(_loopTime);
                }
                catch (ThreadAbortException ex)
                {

                }
                catch (Exception ex)
                {
                    fmService.LogClientException(this.personID,
                        "Error serializing user",
                        ex.Message,
                        (ex.InnerException == null) ? "" : ex.InnerException.Message);                    
                }
            }
            downloadList.Clear();
            AlertMessage("Automatic downloader shut down");
        }

        public void AddDownloadEntityToQueue(AutomaticDownloadEntity entity)
        {
            this.downloadList.Add(entity);
            StartDownloader();
        }

        public void RequestState()
        {
            AlertMessage(String.Format("Thread state {0}, \r\n Usedownloader {1}, \r\n Thread alive {2}", 
                _downloadThred.ThreadState.ToString(), 
                this.settings.UseDownloader,
                _downloadThred.IsAlive));
        }

        public void StartDownloader()
        {
            if (settings.UseDownloader && (_downloadThred == null || !_downloadThred.IsAlive))
            {
                _downloadThred = new Thread(new ThreadStart(RunDownloadJob));
                _downloadThred.Start();
                AlertMessage("Automatic downloader started");
            }
        }

        public void StopDownloader()
        {
            if (_downloadThred != null && _downloadThred.IsAlive)
            {
                _downloadThred.Abort();
            }
        }

        private void AlertMessage(string message)
        {
            if (this.ActionMessage != null)
            {
                this.ActionMessage(message);
            }
        }
    }

    
    public class AutomaticDownloadEntity : CopyEntity
    {
        public AutomaticDownloadEntity(CopyEntity ce, long personID)
        {
            this.CopyDestination = ce.CopyDestination;
            this.FileID = ce.FileID;
            this.FileLength = ce.FileLength;
            this.SourceFileName = ce.SourceFileName;
            this.SourcePath = ce.SourcePath;
            this.personID = personID;
        }
        
        private int retryTimes;
        private long personID;

        public int RetryTimes
        {
            get { return retryTimes; }
            set { retryTimes = value; }
        }

        public long PersonID
        {
            get { return personID; }
            set { personID = value; }
        }
    }
}
